package com.aube.control.player;

import android.content.Context;
import android.media.MediaCodec;
import android.media.MediaPlayer;
import android.net.Uri;
import android.view.Surface;
import android.view.TextureView;

import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.aube.activity.ResolutionHelper;
import com.aube.control.exoplayer.ExoPlayerBuilder;
import com.aube.control.exoplayer.ExtractorRendererBuilder;
import com.aube.control.mediaproxy.MediaServer;
import com.aube.control.mediaproxy.NanoHTTPD;
import com.aube.model.RichVideoResponse;
import com.aube.utils.SaxParamParser;
import com.bokecc.sdk.mobile.play.DWMediaPlayer;
import com.google.android.exoplayer.ExoPlayer;
import com.google.android.exoplayer.MediaCodecTrackRenderer;
import com.google.android.exoplayer.audio.AudioTrack;
import com.google.android.exoplayer.util.Util;
import com.huyn.baseframework.dynamicload.video.AubeVideoPlayer;
import com.huyn.baseframework.model.VideoPlayModel;
import com.huyn.baseframework.net.BFJSONRequest;
import com.huyn.baseframework.net.HttpRequestLoader;
import com.huyn.baseframework.utils.Constant;
import com.huyn.baseframework.utils.StringUtils;
import com.huyn.baseframework.utils.Utils;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;

/**
 * 视频播放的基础逻辑
 * 集成了2种播放器:
 * 1.ExoPlayer
 * 2.MediaPlayer
 * 视频来源有两种:
 * 1.来自cc视频的先根据视频id取到正常的url格式的播放地址
 * 2.来自又拍云
 * Created by huyaonan on 16/4/12.
 */
public abstract class AbstractVideoHelper implements AubeVideoPlayer {

    protected final String TAG = getClass().getSimpleName();

    protected static final int PLAY_WATCH = 10000;

    protected AtomicBoolean isDestroyed = new AtomicBoolean(false);

    protected AtomicBoolean prepared = new AtomicBoolean(false);

    public IPlayCallback iPlayCallback;
    protected Context mContext;
    protected TextureView mTexture;

    protected MediaPlayer player;

    protected VideoPlayModel mVideo;

    protected float mBufferdPercent = 0;
    protected int mLastPosition = 0;
    public float mCachedData = 0;
    public float mDownloadedData = 0;
    public float dataLoaded = 0;
    public boolean isProxyWaiting = false;

    public static final int ILLEGAL_BUFFER_RESULT = -9999;
    public static final int BUFFER_COMPLETE = -9998;
    public static final int PLAY_END = -10000;
    private static final int DEFAULT_RESOLUTION = 10;//默认清晰

    protected boolean enableVolume = true;

    private Map<Integer, String> definitionMap;

    /** 是否开启代理 **/
    public static boolean ENABLE_SOCKET_PROXY = true;
    /** 是否启用EXOPLAYER **/
    public static boolean ENABLE_EXOPLAYER_MODE = true;
    /** 是否不使用CC SDK **/
    public static boolean DO_NOT_USE_CC = true;
    /** 开启低端硬件适配 **/
    public static boolean ENABLE_VIDEO_BALANCE = true;

    private MediaServer mediaServer;
    /** 动态调配网速的开关 **/
    private AtomicBoolean mWaiting = new AtomicBoolean(false);

    protected ISurfaceTextureProvider iProvider;

    private RichVideoResponse mCCVideoModel;

    public ExoPlayerBuilder exoPlayer;
    private boolean playerNeedsPrepare = false;

    private int mCurrentDefinition = DEFAULT_RESOLUTION;

    protected AtomicBoolean mRunningOnBackground = new AtomicBoolean(false);
    protected MyMediaPlayerListener myListener;

    private AtomicBoolean mExoExceptionHappened = new AtomicBoolean(false);

    private static final String TEST_M3U8 = "http://aube-tv-videos.b0.upaiyun.com/hls_test/aube_test.m3u8";

    /**
     * 以下是需要子类去实现的方法
     */
    public abstract boolean play();
    public abstract void pausePlay();
    public abstract void seekTo(int pos);
    public abstract boolean startPlay();
    protected abstract void texturePrepared(Surface surface);
    public abstract int getCachedBuffer();

    public long getStartTime() {
        return 0;
    }

    public void onExoSeekComplete(){}

    public AtomicBoolean mIsAdPlaying = new AtomicBoolean(false);
    public void adFinished() {
        mIsAdPlaying.set(false);
        if(exoPlayer != null) {
            exoPlayer.setPlayWhenReady(true);
        } else {
            readyToPlay();
        }
    }
    public void readyToPlay() {}

    public void initAdState(boolean isPlaying) {
        mIsAdPlaying.set(isPlaying);
    }

    protected void textureForExoPrepared(Surface surface) {
        if(exoPlayer != null && surface != null) {
            exoPlayer.setSurface(surface);
        }
    }

    public boolean isPlaying() {
        if(player != null)
            return player.isPlaying();
        if(exoPlayer != null)
            return exoPlayer.getPlayWhenReady();
        return false;
    }

    public void stopPlayer() {
        if (player != null) {
            player.stop();
            player.reset();
        }

        if(exoPlayer != null) {
            exoPlayer.stop();
        }
    }

    public void releasePlayer() {
        if (player != null) {
            player.release();
            player = null;
        }

        if (exoPlayer != null) {
            Utils.Log("EXO", "exoplayer release");
            mLastPosition = (int) exoPlayer.getCurrentPosition();
            exoPlayer.release();
            exoPlayer = null;
        }
    }

    public boolean isPrepared() {
        return prepared.get();
    }

    public long getCurrentProgress() {
        if(player != null)
            return player.getCurrentPosition();
        if(exoPlayer != null)
            return exoPlayer.getCurrentPosition();
        return 0;
    }

    public long getDuration() {
        if(player != null)
            return player.getDuration();
        if(exoPlayer != null)
            return exoPlayer.getDuration();
        return 0;
    }

    public void reset() {
        if(player != null) {
            player.stop();
            player.reset();
        } else if(exoPlayer != null)
            exoPlayer.reset();
    }

    public String getVideoId() {
        if(mVideo == null)
            return "NULL";
        return mVideo.videoId;
    }

    public void onDestroy() {
        if(mediaServer != null) {
            mediaServer.stop();
        }
    }

    public void makeAWait() {
        mWaiting.compareAndSet(false, true);
//        Utils.Log(getVideoId(), "video should be waiting");
    }

    public void quitWait() {
        mWaiting.compareAndSet(true, false);
//        Utils.Log(getVideoId(), "video should be loading");
    }

    public void startPlay(long progress){}

    public void manageSound(boolean open) {
        enableVolume = open;
        if(player != null) {
            if (open)
                player.setVolume(0.5f, 0.5f);
            else
                player.setVolume(0, 0);
        } else if(exoPlayer != null) {
            exoPlayer.manageSound(open);
        }
    }

    public void reloadVideoIfNeed() {
        if(mExoExceptionHappened.get()) {
            mExoExceptionHappened.set(false);
            Utils.Log(getVideoId(), "---------------------------------------reloadVideoIfNeed");
            //重新初始化
            changeDefinition(mCurrentDefinition, "");
//            if(mVideo.isUrlPlayValid()) {
//                initPlay(mVideo.getPlayUrl());
//                definitionMap = mVideo.getDefinitions();
//                if (definitionMap != null && iPlayCallback != null) {
//                    iPlayCallback.onReceiveDefinition(definitionMap, mVideo.getDefaultDefinition());
//                }
//            } else if(StringUtils.isNotBlank(mVideo.videoNo)) {
//                loadVideoUrlByJson(mVideo.videoNo, DEFAULT_RESOLUTION);
//            }
        }
    }

    public boolean isSoundOn() {
        return enableVolume;
    }

    public void changeDefinition(String des, int definition) {
        try {
            if(player != null) {
                if(player instanceof DWMediaPlayer) {
                    Map<String, Integer> maps = ((DWMediaPlayer) player).getDefinitions();
                    if (maps.containsKey(des)) {
                        prepared.set(false);
                        player.reset();
                        ((DWMediaPlayer) player).setDefinition(mContext, definition);

                        ResolutionHelper.getInstance().updateCode(definition);
                    }
                } else {
                    changeDefinition(definition, des);
                }
            } else if(exoPlayer != null) {
                changeDefinition(definition, des);
            }
        } catch (Exception e) {
            e.printStackTrace();
            Utils.Log(getVideoId(), "play error:" + e.getMessage());
        }
    }

    private void changeDefinition(int definition, String des) {
        Utils.Log(getVideoId(), "change definition..." + definition + "/" + des);
        mCurrentDefinition = definition;
        if(definitionMap != null && definitionMap.containsKey(definition)) {
            //切换分辨率,关闭原先的链接并回收端口
            if(mediaServer != null) {
                mediaServer.stop();
                mediaServer = null;
            }
            prepared.set(false);
            reset();
            if(mVideo.isUrlPlayValid()) {
                initPlay(mVideo.getPlayUrl(definition));
            } else if(StringUtils.isNotBlank(mVideo.videoNo)) {
                if(mCCVideoModel != null) {
                    String url = mCCVideoModel.getOneUrl(definition);
                    if(StringUtils.isNotBlank(url))
                        initPlay(url);
                    else
                        loadVideoUrlByJson(mVideo.videoNo, definition);
                } else {
                    loadVideoUrlByJson(mVideo.videoNo, definition);
                }
            }
            ResolutionHelper.getInstance().updateCode(definition);
        }
    }

    protected void initPlayInfo(Context context, VideoPlayModel model) {
        this.mVideo = model;
        if(DO_NOT_USE_CC) {
//            loadVideoUrlByXml(videoId);
            if(model.isUrlPlayValid()) {
                initPlay(model.getPlayUrl());
                definitionMap = model.getDefinitions();
                if (definitionMap != null && iPlayCallback != null) {
                    iPlayCallback.onReceiveDefinition(definitionMap, model.getDefaultDefinition());
                }
            } else if(StringUtils.isNotBlank(model.videoNo)) {
                loadVideoUrlByJson(model.videoNo, DEFAULT_RESOLUTION);
            }
        } else {
            prepared.set(false);
            player = new DWMediaPlayer();
            player.reset();

            int definition = ResolutionHelper.getInstance().getCode();
            try {
                try {
                    if (definition > 0)
                        ((DWMediaPlayer) player).setDefaultDefinition(definition);
                    else
                        ((DWMediaPlayer) player).setDefaultDefinition(DEFAULT_RESOLUTION);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                ((DWMediaPlayer) player).setVideoPlayInfo(model.videoNo, Constant.CC_USERID, Constant.CC_API_KEY, context);
                player.prepareAsync();
                Utils.Log(getVideoId(), "prepareacync");
            } catch (Exception e) {
                e.printStackTrace();
                Utils.Log(getVideoId(), "player error");
            }
        }
    }

    /**override by TextureVideoHelper**/
    public void initPlayState(boolean play) {}
    public void setStartTime(long startTime) {}
    public void updateProgress(long current) {}

    private ExoPlayerBuilder.RendererBuilder getRendererBuilder(String playUrl) {
        String userAgent = Util.getUserAgent(mContext, "ExoPlayer");
//        if(playUrl.contains("m3u8"))
//            return new HlsRendererBuilder(mContext, userAgent, playUrl);
        return new ExtractorRendererBuilder(mContext, userAgent, Uri.parse(playUrl));
    }

    private void preparePlayer(String playUrl) {
        Utils.Log("EXO", playUrl);
//        playUrl = TEST_M3U8;
        if(iPlayCallback != null && iPlayCallback.getTargetPosition() > 0)
            mLastPosition = (int) iPlayCallback.getTargetPosition();

        if (exoPlayer == null) {
            exoPlayer = new ExoPlayerBuilder(getRendererBuilder(playUrl));
            exoPlayer.addListener(new ExoPlayerBuilder.Listener() {
                @Override
                public void onStateChanged(boolean playWhenReady, int playbackState) {
                    if (playbackState == ExoPlayer.STATE_ENDED) {
                        if(!prepared.get())
                            return;
                        if(iPlayCallback != null)
                            iPlayCallback.endPlay();
                    }
                    String text = "playWhenReady=" + playWhenReady + ", playbackState=";
                    switch(playbackState) {
                        case ExoPlayer.STATE_BUFFERING:
                            text += "buffering";
                            break;
                        case ExoPlayer.STATE_ENDED:
                            text += "ended";
                            break;
                        case ExoPlayer.STATE_IDLE:
                            text += "idle";
                            break;
                        case ExoPlayer.STATE_PREPARING:
                            text += "preparing";
                            break;
                        case ExoPlayer.STATE_READY:
                            text += "ready";
                            if(!prepared.get()) {
                                prepared.set(true);
                                play();
                            } else {
                                //seek complete
                                onExoSeekComplete();
                            }
                            break;
                        default:
                            text += "unknown";
                            break;
                    }
                    Utils.Log("EXO", getVideoId() + "===" + text);
                }

                @Override
                public void onError(Exception e) {
                    if(iPlayCallback != null)
                        iPlayCallback.onError();
                    mExoExceptionHappened.set(true);
                }

                @Override
                public void onVideoSizeChanged(int width, int height, int unappliedRotationDegrees, float pixelWidthHeightRatio) {

                }
            });
            exoPlayer.setInfoListener(new ExoPlayerBuilder.InfoListener() {
                @Override
                public void onDroppedFrames(int count, long elapsed) {
                    float r = count * 1f / elapsed;
                    Utils.Log("EXO", getVideoId() + "===onDroppedFrames:" + count +"/" + elapsed + " ratio:" + r);

                    if(r >= 0.005)
                        MultiVideoBalanceAdapter.getInstance().report(getVideoId());
                }

                @Override
                public void onBandwidthSample(int elapsedMs, long bytes, long bitrateEstimate) {
                    Utils.Log("EXO", getVideoId() + "===onBandwidthSample:" + elapsedMs +"/" + bytes + "/" + bitrateEstimate);
                }

                @Override
                public void onDecoderInitialized(String decoderName, long elapsedRealtimeMs, long initializationDurationMs) {
                    Utils.Log("EXO", getVideoId() + "===onDecoderInitialized:" + decoderName +"/" + elapsedRealtimeMs + "/" + initializationDurationMs);
                }
            });
            exoPlayer.setInternalErrorListener(new ExoPlayerBuilder.InternalErrorListener() {
                @Override
                public void onRendererInitializationError(Exception e) {

                }

                @Override
                public void onAudioTrackInitializationError(AudioTrack.InitializationException e) {

                }

                @Override
                public void onAudioTrackWriteError(AudioTrack.WriteException e) {

                }

                @Override
                public void onAudioTrackUnderrun(int bufferSize, long bufferSizeMs, long elapsedSinceLastFeedMs) {

                }

                @Override
                public void onDecoderInitializationError(MediaCodecTrackRenderer.DecoderInitializationException e) {
                    if(iPlayCallback != null)
                        iPlayCallback.onDecoderInitializationError(mLastPosition);
                }

                @Override
                public void onCryptoError(MediaCodec.CryptoException e) {

                }

                @Override
                public void onLoadError(int sourceId, IOException e) {

                }
            });
            exoPlayer.seekTo(mLastPosition);
            playerNeedsPrepare = true;
            if (playerNeedsPrepare) {
                exoPlayer.prepare();
                playerNeedsPrepare = false;
                manageSound(enableVolume);
            }
        } else {
            exoPlayer.seekTo(mLastPosition);
            playerNeedsPrepare = true;
            if (playerNeedsPrepare) {
                exoPlayer.prepare(getRendererBuilder(playUrl));
                playerNeedsPrepare = false;
                manageSound(enableVolume);
            }
        }
        textureForExoPrepared(getSurface());
        //modify on 2016.11.4 根据广告是否在播放,来设定player是否在onPrepare之后立刻播放
//        exoPlayer.setPlayWhenReady(true);
        exoPlayer.setPlayWhenReady(!mIsAdPlaying.get());
    }

    public void loadVideoUrlByJson(String videoId, final int quanlity) {
        HashMap<String, String> parameterMap = new HashMap<>();
        parameterMap.put("userid", Constant.CC_USERID);
        parameterMap.put("videoid", videoId);

        StringBuffer sb = new StringBuffer();
        String url = "http://p.bokecc.com/servlet/app/playinfo";
        sb.append(url).append(url.endsWith("?") ? "" : "?");
        Map<String, String> postdata = SaxParamParser.doTHQS(parameterMap);

        for (Map.Entry<String, String> item : postdata.entrySet()) {
            sb.append("").append(item.getKey()).append("=").append(item.getValue()).append("&");
        }

        String requestUrl = sb.toString();
        BFJSONRequest request = new BFJSONRequest(requestUrl, RichVideoResponse.class, new Response.Listener<RichVideoResponse>() {

            @Override
            public void onResponse(RichVideoResponse response) {
                if(response != null) {
                    mCCVideoModel = response;

                    String url = response.getOneUrl(quanlity);
                    if(StringUtils.isNotBlank(url)) {
                        initPlay(url);
                    }

                    definitionMap = response.getResolutionMap();
                    if (definitionMap != null && iPlayCallback != null) {
                        iPlayCallback.onReceiveDefinition(definitionMap, quanlity);
                    }
                }
            }

            @Override
            public void onErrorResponse(VolleyError error) {
            }

            @Override
            public void onStart() {
            }
        });
        HttpRequestLoader.getInstance(mContext).startHttpLoader(request);
    }

    private void initPlay(String url) {
        try {
            Utils.Log(getVideoId(), "play url..." + url);
//            url = TEST_M3U8;
            //开启负载均衡
            if(ENABLE_SOCKET_PROXY) {
                //IMPORTANT 在负载均衡启动的时候,永远要初始化为false,不然会取上一次的数据,比如在切换分辨率的时候会限速导致太慢
                mWaiting.set(false);
                //切换分辨率后,关闭原先的链接并回收端口
                if (mediaServer != null) {
                    mediaServer.stop();
                    mediaServer = null;
                }

                mediaServer = new MediaServer(url);
                mediaServer.setOnBufferingUpdateListener(new NanoHTTPD.OnBufferingUpdateListener() {
                    @Override
                    public void onBufferingUpdate(float cachedData, float downloadProgress) {
                        mCachedData = cachedData;
                        mDownloadedData = downloadProgress;
//                        Utils.Log(getVideoId(), "-----progress..." + downloadProgress);
                    }

                    @Override
                    public void updateSpeed(float speed, boolean isWaiting) {
                        dataLoaded += speed;
                        isProxyWaiting = isWaiting;
                    }

                    @Override
                    public boolean getProxyState() {
                        return mWaiting.get();
                    }
                });
                mediaServer.setVideoId(getVideoId());
                int port = mediaServer.start();
                url = getIpAccess() + port + "/index.html";
            }

            if(ENABLE_EXOPLAYER_MODE) {
                preparePlayer(url);
            } else {
                play(url);
            }
        } catch (Exception e) {
            e.printStackTrace();

            //当代理模式不可用的时候,走正常的播放流程
            try {
                if (ENABLE_EXOPLAYER_MODE) {
                    preparePlayer(url);
                } else {
                    play(url);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }

    private String getIpAccess() {
//        WifiManager wifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
//        int ipAddress = wifiManager.getConnectionInfo().getIpAddress();
//        final String formatedIpAddress = String.format("%d.%d.%d.%d", (ipAddress & 0xff), (ipAddress >> 8 & 0xff),
//                (ipAddress >> 16 & 0xff), (ipAddress >> 24 & 0xff));
//        return "http://" + formatedIpAddress + ":";
        return "http://127.0.0.1:";
    }

    private void play(String url) {
        String videoUrl = url;
        if(player == null) {
            prepared.set(false);
            player = new MediaPlayer();
            player.reset();
            manageSound(enableVolume);
        }

        try {
            player.setDataSource(videoUrl);
            player.prepareAsync();
            Utils.Log(getVideoId(), "prepareacync") ;

            texturePrepared(getSurface());
        } catch (Exception e) {
            e.printStackTrace();
            Utils.Log(getVideoId(), "player error");
        }
    }

    public Surface getSurface() {
        if(mTexture != null && mTexture.isAvailable()) {
            Utils.sysout("=========================getsurface from mTexture..." + (mTexture.getSurfaceTexture()));
            return new Surface(mTexture.getSurfaceTexture());
        } else if(iProvider != null) {
            Utils.sysout("=========================getsurface from iProvider..." + (iProvider.getSurface()==null));
            return iProvider.getSurface();
        }
        return null;
    }

    public void setPlayCallback(IPlayCallback callback) {
        this.iPlayCallback = callback;
    }

    public void updateSurface(Surface surface) {
        Utils.sysout("=========================updateSurface..." + (surface==null));
        if(player != null)
            player.setSurface(surface);
        else if(exoPlayer != null) {
            exoPlayer.setSurface(surface);
        }
    }

    protected void pausePlayer() {
        if(player != null) {
            Utils.Log(TAG, "PLAYER NOT NOULL:" + player.getClass().getName());
            player.pause();
        } else if(exoPlayer != null) {
            Utils.Log(TAG, "pause exo");
            exoPlayer.setPlayWhenReady(false);
//            exoPlayer.blockingClearSurface();
        }
    }

    protected void startPlayer() {
        if(player != null)
            player.start();
        else if(exoPlayer != null)
            exoPlayer.setPlayWhenReady(true);
    }

    protected void seekPlayer(int position) {
        if(player != null)
            player.seekTo(position);
        else if(exoPlayer != null)
            exoPlayer.seekTo(position);
    }

    public interface IPlayCallback {
        public void startPlay();
        public void endPlay();
        public void updatePlay(long current, long duration);
        public void updateBuffer(int progress);
        public boolean hasFocused();
        public void onError();
        public void clearLoading();
        public void onReceiveDefinition(Map<Integer, String> definitions, int currentCode);
        public void syncSuccess();

        public void onDecoderInitializationError(long position);
        public long getTargetPosition();
    }

}
